﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices; // to call the C DLL via DllImport
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;

namespace ink.Network
{
    public delegate void StringDelegate(object sender, UInt64 from, string data);
    public class vastAPI
    {
        // make sure LoadLibrary is usable 
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr LoadLibrary(string lpFileName);

        // load functions exported by VASTwrapperC.dll
        [DllImport("VASTwrapperC")]
        private static extern int InitVAST(bool is_gateway, String str);

        [DllImport("VASTwrapperC")]
        private static extern int ShutVAST();

        [DllImport("VASTwrapperC")]
        private static extern bool VASTJoin(float x, float y, ushort radius);

        [DllImport("VASTwrapperC")]
        private static extern bool VASTLeave();

        [DllImport("VASTwrapperC")]
        private static extern bool VASTMove(float x, float y);

        [DllImport("VASTwrapperC")]
        private static extern uint VASTTick(uint time_budget);

        [DllImport("VASTwrapperC")]
        private static extern bool VASTPublish(String msg, uint size, ushort radius);

        [DllImport("VASTwrapperC")]
        //public static extern bool VASTReceive (ref String msg, ref uint size, ref UInt64 from);
        private static extern IntPtr VASTReceive(ref uint size, ref UInt64 from);

        [DllImport("VASTwrapperC")]
        private static extern bool isVASTInit();

        [DllImport("VASTwrapperC")]
        private static extern bool isVASTJoined();

        [DllImport("VASTwrapperC")]
        private static extern ulong VASTGetSelfID();

        private static AutoResetEvent rwlock = null;
        private static AutoResetEvent bw_done = null;
        private bool bVASTInited = false;
        private bool bVASTReleased = false;
        private BackgroundWorker bw = null;

        private struct vastPackData
        {
            public UInt64 strFrom;
            public string strData;
        };

        List<vastPackData> myRcvQueue;
        List<vastPackData> mySndQueue;

        public event StringDelegate OnDataReceived;

        public bool startVAST(string strIP, string strPort, bool bGetWay)
        {
            if (bVASTInited)
                return false;

            //init default var...
            rwlock = new AutoResetEvent(true);

            //init vast...
            rwlock.WaitOne();
            {
                bw_done = new AutoResetEvent(false);
                bw = new BackgroundWorker();
                myRcvQueue = new List<vastPackData>();
                mySndQueue = new List<vastPackData>();

                //bool bquit = false;
                try
                {
                    InitVAST(bGetWay, strIP + ":" + strPort);
                    VASTJoin(100, 100, 200);
                }
                catch (DllNotFoundException e)
                {
                    MessageBox.Show(e.ToString(), "VAST DLL File not found.");

                    bw_done.Dispose();
                    bw.Dispose();
                    myRcvQueue.Clear();
                    mySndQueue.Clear();

                    rwlock.Set();
                    rwlock.Dispose();
                    return false;

                }
                catch
                {
                    MessageBox.Show("vast init error.", "VAST DLL error.");

                    bw_done.Dispose();
                    bw.Dispose();
                    myRcvQueue.Clear();
                    mySndQueue.Clear();

                    rwlock.Set();
                    rwlock.Dispose();
                    return false;
                }

                //init backgroundworker for working
                bw.WorkerSupportsCancellation = true;
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                //bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

                bVASTInited = true;

            }
            rwlock.Set();

            if (bVASTInited)
                bw.RunWorkerAsync();

            return true;
        }

        ~vastAPI()
        {
            releaseVAST();
        }

        public bool releaseVAST()
        {
            if (bVASTInited == false)
            {
                return true;
            }

            if (bVASTReleased == false)
            {
                //...
                if (bw.WorkerSupportsCancellation == true)
                    bw.CancelAsync();

                bw_done.WaitOne();

                    
                rwlock.WaitOne();
                {
                    //CLOSE VAST
                    try
                    {
                        VASTLeave();
                        ShutVAST();
                    }
                    catch
                    {
                        MessageBox.Show("VASTLeave(), ShutVAST() error.", "VAST DLL error.");
                    }

                    bw_done.Dispose();
                    bw.Dispose();
                    myRcvQueue.Clear();
                    mySndQueue.Clear();

                    GC.Collect(); //COLLECT MEMORY


                    bVASTReleased = true;
                    bVASTInited = false;

                }
                rwlock.Set();
            }
            

            rwlock.Dispose();
            return true;
        }

        public bool sndMessage(string strMessage)
        {
            if (this.bVASTInited == false)
                return false;

            rwlock.WaitOne();
            vastPackData pd = new vastPackData();
            pd.strFrom =VASTGetSelfID();
            pd.strData =strMessage;
            this.mySndQueue.Add(pd);
            rwlock.Set();

            return true;
        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            while (worker.CancellationPending == false)
            {
                rwlock.WaitOne();
                {
                    try
                    {
                        VASTTick(0);
                        VASTMove(100, 100);
                    }
                    catch
                    {
                        MessageBox.Show("VASTTick(), VASTMove() error.", "VAST DLL error.");
                        rwlock.Set();
                        e.Cancel = true;
                        return;
                    }

                }
                rwlock.Set();

                System.Threading.Thread.Sleep(25);

                //rcv form vast
                UInt64 from = 0;
                uint size = 0;

                if (worker.CancellationPending == true)
                    break;

                rwlock.WaitOne();
                {
                    while (true)
                    {

                        try
                        {
                            IntPtr ptr = VASTReceive(ref size, ref from);

                            if (size == 0 && from == 0)
                                break;

                            if (from != VASTGetSelfID())
                            {
                                string str0 = Marshal.PtrToStringAnsi(ptr);

                                string str1 = str0.Substring(0, (int)size);

                                vastPackData vpd = new vastPackData();
                                vpd.strData = str1;
                                vpd.strFrom = from;
                                this.myRcvQueue.Add(vpd);
                            }

                        }
                        catch
                        {
                            MessageBox.Show("VASTReceive() error.", "VAST DLL error.");
                            rwlock.Set();
                            e.Cancel = true;
                            return;
                        }

                    }
                }
                rwlock.Set();

                System.Threading.Thread.Sleep(25);

                //snd from queue
                if (worker.CancellationPending == true)
                    break;

                rwlock.WaitOne();
                {
                    if (mySndQueue.Count > 0)
                    {
                        string msg = mySndQueue[0].strData;
                        mySndQueue.RemoveAt(0);

                        try
                        {
                            VASTPublish(msg, (uint)msg.Length, 0);
                        }
                        catch
                        {
                            MessageBox.Show("VASTPublish() error.", "VAST DLL error.");
                            rwlock.Set();
                            e.Cancel = true;
                            return;
                        }

                    }
                    

                }
                rwlock.Set();

                //trigger onrcvdata
                if (OnDataReceived != null)
                {
                    while (myRcvQueue.Count >0)
                    {
                        if (worker.CancellationPending == true)
                            break;

                        rwlock.WaitOne();
                            vastPackData tmpPD =myRcvQueue[0];
                            myRcvQueue.RemoveAt(0);
                        rwlock.Set();

                        OnDataReceived( this, tmpPD.strFrom, tmpPD.strData );
                    }
                    
                }

            }

            bw_done.Set(); //quit thread
        }

        //private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        //{

            
        //}

    }
}
